/***
 * 手写一个Promise
 *  1、promise的状态有三种padding默认 fufilled成功 rejected失败
 *  2、构造器中立即执行传递的匿名函数
 *     如果抛出异常就走异常失败方法，如果两个方法都有调用只会走前面的方法
 *  3、每一个实例都有一个then方法
 *    a、then方法接收两个形参，一个是成功状态的方法onfufilled一个是失败状态的方法onRejected
 *    b、根据当前实例的对应状态调用对应的状态方法
 *  4、匿名函数中接收两个参数都是方法，一个是成功的方法resolve，另外一个是失败的方法reject
 *    a、每一个方法中都传递了参数，参数的类型可以是任意类型
 *    b、成功/失败的方法中更新对应的状态且当前的状态必须是padding状态
 *    c、形参方法接收的传递的对应的值
 *    d、方法是立即执行的
 *    e、在匿名方法的函数中可能存在着异步方法，异步中执行成功和失败的回调
 *       由于then方法是同步方法，不会走任何的成功和失败的回调状态方法
 *       解决方法：
 *       根据状态（异步的时候Promise的状态为Padding）可以将then的形参进行缓存，在更新状态的时候在将这些缓存的函数
 *       全部执行
 *  5、链式调用
 *    a、每一then方法都会返回一个新的Promise,每一个then方法的后面都可以在then
 *       then方法中可能有返回值，需要将这个值传递给下一个then方法中去。
 *    b、then中的形参函数的返回值为一个promise的实例（也就是返回值x）
 *       同时这个里面要注意如果返回值跟当前实例是同一个实例，需要抛出异常
 *    c、then函数中没有传递函数需要给定默认函数
 *
 *  6、静态方法 后面都可以在进行.then方法 相当于return new Promise(...)
 *    a、Promise.resolve 方法
 *       传递的形参可以是一个Promise对象或者多层嵌套的Promise对象，
 *       需要在调用实例上的resolve方法的时候进行判断如果是一个Promise对象则进行递归调用解析出
 *       里层的值，then方法可以公用解析Promise递归
 *    b、Promise.all 方法
 *      all方法可以接收多个Promise的实例接收的参数为一个数组,元素可以为任意类型
 *      如果其中有Promise且有失败，最后的结果就是一个失败，调用then当中的失败方法
 *
 */
const STATUS = {
  PADDING: "PADDING",
  FUFILLED: "FUFILLED",
  REJECT: "REJECT",
};

class Promise {
  constructor(executor) {
    this.status = STATUS.PADDING;
    this.value = "";
    this.reason = "";
    this.onFulfilled = []; //成功的回调方法
    this.onRejected = []; //失败的回调方法
    const resolve = (val) => {
      //这里的this.status与val中的status不是同一个值
      if (this.status == STATUS.PADDING) {
        if (val instanceof Promise) {
          //这个地方必须return 需要递归嵌套的Promise
          return val.then(resolve, reject);
        }
        this.value = val;
        this.status = STATUS.FUFILLED;
        //处理异步回调的问题
        this.onFulfilled.forEach((fn) => {
          fn();
        });
      }
    };
    const reject = (val) => {
      if (this.status == STATUS.PADDING) {
        this.reason = val;
        this.status = STATUS.REJECT;
        //异步回调
        this.onRejected.forEach((fn) => {
          fn();
        });
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled =
      typeof onFulfilled == "function" ? onFulfilled : (data) => data;
    //这个地方必须是 throw err 不然测试不通过
    onRejected =
      typeof onRejected == "function"
        ? onRejected
        : (err) => {
            throw err;
          };
    //Promise形参的匿名方法是同步方法
    let promise2 = new Promise((resolve, reject) => {
      //成功的回调
      if (this.status == STATUS.FUFILLED) {
        //要实例化完才能够获取到promise2
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(x, promise2, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
      //失败的回调
      if (this.status == STATUS.REJECT) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(x, promise2, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
      /**核心处理异步的问题
       * 匿名函数中存在异步调用形参方法，这个时候的状态就是padding
       * 这个时候需要将then方法中的实参函数给保持起来
       * 在更新状态的时候在执行这些函数
       * **/
      //初始状态
      if (this.status == STATUS.PADDING) {
        //AOP
        this.onFulfilled.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(x, promise2, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
        this.onRejected.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(x, promise2, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }
    });
    return promise2;
  }
  static resolve(val) {
    return new Promise((resolve, reject) => {
      resolve(val);
    });
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  }
  static all(promises = []) {
    return new Promise((resolve, reject) => {
      let result = [],
        times = 0;
      function processData(i, val) {
        result[i] = val;
        //要用次数做比较
        if (++times == promises.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < promises.length; i++) {
        let p = promises[i];
        if (typeof p.then == "function") {
          p.then(
            (data) => {
              processData(i, data);
            },
            (err) => {
              reject(err);
            }
          );
        } else {
          processData(i, p);
        }
      }
    });
  }
  catch(err) {
    //后面还可以点then
    return this.then(null, err);
  }
}
//then 形参函数返回值的检查
function resolvePromise(x, promise2, resolve, reject) {
  if (x == promise2) {
    return reject(new TypeError("不能引用自身"));
  }
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    let called;
    try {
      let then = x.then;
      if (typeof then == "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            //递归遍历可能会有嵌套的promise实例
            resolvePromise(y, promise2, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    resolve(x);
  }
}
//Promise A+ 规范测试入口
//需要先安装这个包npm install promises-aplus-tests -g
//命令行进入到文件中执行命令promises-aplus-tests 文件名
Promise.defer = Promise.deferred = function () {
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};

module.exports = Promise;
